package it.univaq.di.helpdesk.business.common.impl.ibatis;

import it.univaq.di.helpdesk.business.common.UserService;
import it.univaq.di.helpdesk.business.errors.AccountLockedException;
import it.univaq.di.helpdesk.business.errors.BusinessException;
import it.univaq.di.helpdesk.business.model.Role;
import it.univaq.di.helpdesk.business.model.User;

import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.ibatis.sqlmap.client.SqlMapClient;

public class IBATISUserService implements UserService {
	private SqlMapClient sqlMap;
	
	public IBATISUserService(SqlMapClient sqlMap) {
		super();
		this.sqlMap = sqlMap;
	}

	@Override
	public User authenticate(String username, String password)
			throws AccountLockedException,BusinessException {
		
		User user = null;
		Map<String,String> param = new HashMap<String, String>();
		param.put("username", username);
		param.put("password", password);
		
		try {
			user = (User) sqlMap.queryForObject("authUser", param);
		} catch (SQLException e) {
			throw new BusinessException(e.getMessage(), e.getCause());
		}
		
		if (user != null && !user.isEnabled()) {
			throw new AccountLockedException("User account has been locked.");
		}
		
		return user;
	}

	@Override
	public void addProfile(User user) throws BusinessException {
		try {
			sqlMap.startTransaction();
			sqlMap.startBatch();
			
			// Inserisce prima il profilo e poi l'utente per ottenere l'id del profilo da inserire nell'utente
			sqlMap.insert("insertUserProfile", user.getProfile());
			sqlMap.update("updateUser", user);
			
			sqlMap.executeBatch();
			sqlMap.commitTransaction();
		} catch (SQLException e) {
			throw new BusinessException(e.getMessage(),	e);
		} finally {
			try {
				sqlMap.endTransaction();
			} catch (SQLException e) {}
		}
	}
	
	@Override
	public void editProfile(User user) throws BusinessException {
		try {
			sqlMap.startTransaction();
			sqlMap.startBatch();
			
			sqlMap.update("updateUser", user);			
			sqlMap.update("updateUserProfile", user.getProfile());
			
			sqlMap.executeBatch();
			sqlMap.commitTransaction();
		} catch (SQLException e) {
			throw new BusinessException(e.getMessage(),	e);
		} finally {
			try {
				sqlMap.endTransaction();
			} catch (SQLException e) {}
		}		
	}

	@Override
	public User findUserByID(int ID) throws BusinessException {
		User user = null;

		try {
			user = (User) sqlMap.queryForObject("User.getUserByID", ID);
		} catch (SQLException e) {
			throw new BusinessException(e.getMessage(),
					e.getCause());
		}
		return user;
	}

	@Override
	public List<Role> findAllRoles() throws BusinessException {
		List<Role> roles = null;

		try {
			roles = (List<Role>) sqlMap.queryForList("getRoles");
		} catch (SQLException e) {
			throw new BusinessException(e.getMessage(),
					e.getCause());
		}
		return roles;
	}
	
	@Override
	public List<User> findAllTechnics() throws BusinessException {
		List<User> technics = null;
		
		try {
			technics = (List<User>) sqlMap.queryForList("getAllTechnics");
		} catch (SQLException e) {
			throw new BusinessException(e.getMessage(),	e);
		}
		return technics;
	}

	@Override
	public List<User> findAllUsers() throws BusinessException {
		List<User> users = null;
		
		try {
			users = (List<User>) sqlMap.queryForList("getAllUsers");
		} catch (SQLException e) {
			throw new BusinessException(e.getMessage(),	e);
		}
		return users;
	}

	@Override
	public void addUser(User user) throws BusinessException {
		try {
			Map<String, String> userRole = new HashMap<String, String>();
			sqlMap.startTransaction();
			sqlMap.startBatch();
			
			sqlMap.insert("addUser", user);
			
			// L'id dell'utente è sempre lo stesso, quindi non si itera su di esso
			userRole.put("userid", Integer.toString(user.getUserID()));
			
			// Iterazione sui gruppi assegnati all'utente
			for (Role role : user.getRoles()) {
				userRole.put("rolename", role.getName());
				
				sqlMap.insert("addUserRoles", userRole);
				
				userRole.remove("rolename");
			}
			
			sqlMap.executeBatch();
			sqlMap.commitTransaction();
		} catch (SQLException e) {
			throw new BusinessException(e.getMessage(),	e);
		} finally {
			try {
				sqlMap.endTransaction();
			} catch (SQLException e) {}
		}
		
	}

	@Override
	public boolean isUsernamePresent(String userName, int userID) throws BusinessException {
		User user = null;
		
		try {
			user = (User) sqlMap.queryForObject("isUsernamePresent", userName);
			
			/* Se l'id delll'utente risultato della query è uguale all'id ricevuto come parametro
			 * si sta effettuando un update e quindi è consetito avere lo stesso username
			 */
			if (user != null) {	
				if (user.getUserID() == userID) user = null;
			}
		} catch (SQLException e) {
			throw new BusinessException(e.getMessage(),	e);
		}
		
		return (user != null); 
	}

	@Override
	public List<User> searchForUsers(Map<String, String> queryParam)
			throws BusinessException {
		List<User> users = null;

		try {
			users = (List<User>) sqlMap.queryForList("searchForUser", queryParam);
		} catch (SQLException e) {
			throw new BusinessException(e.getMessage(),	e);
		}
		
		return users;
	}
	
	@Override
	public void editUser(User user) throws BusinessException {
		try {
			Map<String, String> userRole = new HashMap<String, String>();
			
			sqlMap.startTransaction();
			sqlMap.startBatch();
			
			sqlMap.update("updateUser", user);
			
			// Rimuovo tutti i gruppi a cui l'utente corrente è associato
			sqlMap.delete("deleteUserRoles",user.getUserID());
			
			// L'id dell'utente è sempre lo stesso, quindi non si itera su di esso
			userRole.put("userid", Integer.toString(user.getUserID()));
			
			// Iterazione sui gruppi assegnati all'utente
			for (Role role : user.getRoles()) {
				userRole.put("rolename", role.getName());
				
				sqlMap.insert("addUserRoles", userRole);
				
				userRole.remove("rolename");
			}
			
			sqlMap.executeBatch();
			sqlMap.commitTransaction();
		} catch (SQLException e) {
			throw new BusinessException(e.getMessage(),	e);
		} finally {
			try {
				sqlMap.endTransaction();
			} catch (SQLException e) {}
		}
	}

	@Override
	public void lockUnlockUser(User user) throws BusinessException {
		try {
			sqlMap.update("lockUnlockUser", user);
		} catch (SQLException e) {
			throw new BusinessException(e.getMessage(),	e);
		}
	}
}
